// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build helenos

#include <futex.h>

#include "runtime_types.h"
#include "lock.h"
#include "atomic.h"
#include "panic.h"

#define SIGNALED ((uintptr) -1)

void runtime_lock(Lock * l)
{
	// Check for initialization.
	uint32 init = runtime_xchg(&l->init, 1);
	if (init == 0)
		futex_initialize(&l->futex, 1);

//	// Tell runtime there is a new lock/request.
//	M *m = runtime_m();
//	if (m->locks++ < 0)
//		runtime_throw("runtime_lock: lock count");

	futex_down(&l->futex);
}

void runtime_unlock(Lock * l)
{
//	// Tell runtime there is one less lock held.
//	if (--runtime_m()->locks < 0)
//		runtime_throw("runtime_unlock: lock count");

	futex_up(&l->futex);
}

// One-time notifications.
void runtime_noteclear(Note * n)
{
	*n = 0;
}

void runtime_notewakeup(Note * n)
{
	uintptr old;

	do {
		old = *n;
	} while (!runtime_cas(n, old, SIGNALED));

	if (old == SIGNALED) {
		runtime_throw("runtime_notewakeup() called more than once");
	}

	futex_t *f = (futex_t *) old;

	if (f != NULL) {
		futex_up(f);
	}
}

void runtime_notesleep(Note * n)
{
	uintptr old;
	futex_t futex = {1};

	do {
		old = *n;

		if (old == SIGNALED) {
			return;
		}

		if (old != 0) {
			runtime_throw("runtime_notesleep() called by more than one goroutine");
		}
	} while (!runtime_cas(n, old, (uintptr)&futex));

	// Registered, sleep on futex.
	futex_down(&futex);
}
